using System;
using System.Data;
using System.Text;

using Schemata.Core;

namespace Awaken.CodeGen
{
	/// <summary>
	/// Summary description for Table.
	/// </summary>
	public class Table
	{
		protected readonly static string Tab = "\t";
		protected readonly static string NewLine = "\r";
		protected TableSchema component;

		public TableSchema Component
		{
			get{ return component; }
			set{ component=value; }
		}

		public string TableName
		{
			get{ return Component.Name; }
		}

        protected Database database;
        public Database Database
        {
            get 
            {
                if (database == null)
                {
                    database = new Database(Component.Database);
                }
                return database;
            }
        }

        protected string PascalCaseTableName
		{
			get{ return TableName[0].ToString().ToUpper()+TableName.Substring(1); }
		}

		protected string CamelCaseTableName
		{
			get{ return TableName[0].ToString().ToLower()+TableName.Substring(1); }
		}

		public string CamelCaseClassName
		{
			get{ return ClassName[0].ToString().ToLower()+ClassName.Substring(1); }
		}

        public string ClassName
		{
			get
			{
				string className = PascalCaseTableName;
				
				// Remove spaces.
				className = className.Replace(" " , "");
				className = className.Replace("_" , "");

				// Remove table prefixes.
				className = className.Replace("tbl_" , "");

				// Make singular.
				if( className.EndsWith("s") && !className.EndsWith("ies") )
				{
					int len = className.Length-1;
					className = className.Substring( 0 , len );
				}

				// Make singular.
				if( className.EndsWith("ies") )
				{
					int len =  className.Length-3;
					className = className.Substring( 0 , len );
                    className += "y";
				}

				return className;
			}
		}

        public string CollectionName
        {
            get { return ClassName + "List";  }
        }

        public string GeneratorClass
        {
            get
            {
                string generatorClass = string.Empty;

                if (IdColumns.Count == 1)
                {
                    switch (IdColumns[0].Component.DataType)
                    {
                        case (DbType.Int32): generatorClass = "native"; break;
                        case (DbType.Guid) : generatorClass = "guid.comb"; break;
                        default: generatorClass = "assigned"; break;
                    }
                }

                return generatorClass;
            }
        }

		public Table( TableSchema tableSchema )
		{
			Component = tableSchema;
		}

        public ColumnList IdColumns
        {
            get
            {
                ColumnList list = new ColumnList();
                foreach (Column column in Columns )
                {
                    if (column.IsPrimaryKey)
                    {
                        list.Add(column);
                    }
                }
                return list;
            }
        }

        public ColumnList NonIdColumns
        {
            get
            {
                ColumnList list = new ColumnList();
                foreach (Column column in Columns)
                {
                    if (!column.IsPrimaryKey)
                    {
                        list.Add(column);
                    }
                }
                return list;
            }
        }

        public ManyToManyList ManyToManys
        {
            get
            {
                ManyToManyList list = new ManyToManyList();

                // Ex. Currently Territories.

                // Ex. Tables: Employees, Territories, EmployeeTerritories
                
                // Ex. EmployeeTerritories
                foreach (Table table in Database.ManyToManyTables)
                {
                    // Ex: EmployeeID
                    Column firstKey = table.IdColumns[0];
                    
                    // Ex: TerritoryID
                    Column secondKey = table.IdColumns[1];
                    
                    Column otherKey = null;

                    foreach (Column pk in this.IdColumns)
                    {
                        // Ex. If EmployeeID, link via TerritoryID
                        if (pk.ColumnName == firstKey.ColumnName)
                        {
                            otherKey = secondKey;
                            break;
                        }

                        // Ex. If TerritoryID, link via EmployeeID
                        if (pk.ColumnName == secondKey.ColumnName)
                        {
                            // Ex. Link via EmployeeID
                            otherKey = firstKey;
                            break;
                        }
                    }

                    // Ex. EmployeeID
                    if (otherKey != null)
                    {
                        Table otherTable = null;

                        foreach (Table tbl in Database.SingleIdTables)
                        {
                            foreach (Column pk in tbl.IdColumns)
                            {
                                // Ex. EmployeeID==EmployeeID
                                if (otherKey.ColumnName == pk.ColumnName)
                                {
                                    otherTable = tbl;
                                    break;
                                }
                            }
                        }                        

                        // Ex. Other table = Employee
                        if (otherTable == null)
                        {
                            throw new ApplicationException("Unable to find other table in MM relation: col=" + otherKey.ColumnName);
                        }

                        ManyToMany mm = new ManyToMany();
                        mm.ManyToManyTable = table;
                        mm.CurrentTable = this;
                        mm.OtherTable = otherTable;
                        list.Add(mm);
                    }
                }

                   
                return list;
            }
        }

        protected OneToManyList GetOneToManysByColumnName()
        {
            OneToManyList list = new OneToManyList();

            // Ex. Currently Categories
            
            // Ex. Products, Categories
            foreach (Table table in Database.SingleIdTables)
            {
                // Ex. If Categories==Categories, move along
                if (table.TableName == this.TableName)
                    continue;

                // Ex. If Products, loop over non-id columns
                foreach (Column fk in table.NonIdColumns)
                {
                    // Ex. Loop over each column in Categories
                    foreach (Column pk in this.IdColumns)
                    {
                        // Ex. If Categories.CategoryID
                        if (fk.ColumnName == pk.ColumnName)
                        {
                            OneToMany om = new OneToMany();
                            om.CurrentTable = this;
                            om.OtherTable = table;
                            om.CurrentTableColumn = fk;
                            om.OtherTableColumn = pk;
                            list.Add(om);
                        }
                    }
                }
            }
            return list;
        }

        public OneToManyList GetOneToManysByForeignKey()
        {
            OneToManyList list = new OneToManyList();

            foreach( TableKeySchema fk in this.Component.ForeignKeys )
            {
                OneToMany om = new OneToMany();
                om.CurrentTable = this;
                om.OtherTable = new Table(fk.PrimaryKeyTable);
                om.CurrentTableColumn = new Column(fk.ForeignKeyMemberColumns[0]);
                list.Add(om);
            }

            return list;
        }

        public OneToManyList OneToManys
        {
            get
            {
                //return GetManyToOnesByForeignKey();
                return GetOneToManysByColumnName();
            }
        }

        public ManyToOneList ManyToOnes
        {
            get 
            {
                ManyToOneList list = new ManyToOneList();

                foreach (Column fk in this.NonIdColumns)
                {
                    foreach (Table table in Database.SingleIdTables)
                    {
                        foreach (Column pk in table.IdColumns)
                        {
                            if (pk.ColumnName == fk.ColumnName)
                            {
                                ManyToOne mo = new ManyToOne();
                                mo.CurrentTable = this;
                                mo.OtherTable = table;
                                mo.CurrentTableColumn = fk;
                                mo.OtherTableColumn = pk;
                                list.Add(mo);
                            }
                        }
                    }
                }

                return list;
            }
        }

        public ColumnList Columns
		{
			get
			{
				ColumnList list = new ColumnList();
				foreach( ColumnSchema columnSchema in Component.Columns )
				{
					Column column = new Column( columnSchema );
					list.Add( column );
				}
				return list;
			}
		}

        
	}
}
